<template>
  <div class="protocol-parser">
    <div class="bottom-container">
      <div class="left-panel">
        <div class="panel-content">
          <div class="left-panel-title">
            <el-button type="default" @click="handleBack">返回</el-button>
            <h3>协议结构（小端序）: {{ protocolItem.protocolName }}</h3>
          </div>
          <div class="textarea-wrapper">
            <textarea v-model="protocolText" class="input-area"></textarea>
          </div>

          <!-- 输入字节流 -->
          <h3>待解析字节（hex，空格分隔或紧凑格式均可）：</h3>
          <div class="textarea-wrapper small">
            <textarea v-model="hexInput" class="input-area"></textarea>
          </div>

          <el-button type="default" @click="parse">解析</el-button>

          <h3>校验结果：</h3>
          <div v-if="errors.length === 0">✔ 没有错误</div>
          <ul v-else>
            <li v-for="e in errors" :key="e" style="color:red">{{ e }}</li>
          </ul>
        </div>
      </div>

      <div class="right-panel">
        <div class="panel-content">
          <h3>结果输出：</h3>
          <div class="textarea-wrapper">
            <textarea v-model="consoleLog" class="input-area"></textarea>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script lang="ts" setup>
import {computed, onMounted, ref} from "vue";
import {useRoute} from "vue-router";
import type {ProtocolItemDO} from "@/type/toolLibs/protocol";
import {getProtocolItemById} from "@/api/toolLibs/protocolLib";
import {message} from "@/utils/ElmessasgeUtils";
import router from "@/router";

const route = useRoute()
const protocolId = route.query.protocolId as string;

// 协议内容
const protocolItem = ref<ProtocolItemDO>({
  id: '',
  protocolName: '',

  createTime: '',
  createUserId: '',
  createUserName: '',
  createNickName: '',

  updateTime: '',
  updateUserId: '',
  updateUserName: '',
  updateNickName: '',
  protocolContent: {
    name: '',
    fields: [],
    type: ''
  }
});

const protocolText = computed(() => {
  return JSON.stringify(protocolItem.value.protocolContent.fields, null, 2)
})

function handleBack() {
  router.back();
}

let consoleLog = ""

const hexInput = ref(
    `29 5a 3d 00 12 da 03 03 00 20 05 32 a4 00 03 01 38 a7 ac 00 00 00 00 00 64 00 00 00 00 01 04 00 66 d4 d8 00 04 28 08 01 04 00 01 02 d4 d8 00 04 28 00 14 01 00 02 00 03 00 00 00 00 00 00 00 00 00 00  `
);

const errors = ref([]);
const basic = ["u8", "u16", "u32", "bytes", "string"];

// -----------------------------------------------------------
// 协议结构校验器
// -----------------------------------------------------------
function validateProtocolStructure(struct: any) {
  const errs = [];
  const seen = new Set();

  for (const field of struct) {
    seen.add(field.name);

    if (basic.includes(field.type)) {
      if (!field.size) {
        errs.push(`基础类型字段 ${field.name} 必须包含 size`);
      }
    }

    if (field.type === "array") {
      if ("size" in field && field.size) {
        errs.push(`数组字段 ${field.name} 不应包含 size`);
      }
      if (!field.count_from) {
        errs.push(`数组字段 ${field.name} 必须包含 count_from`);
      }
      if (!seen.has(field.count_from)) {
        errs.push(`数组字段 ${field.name} 的 count_from "${field.count_from}" 未在之前定义`);
      }
      if (!Array.isArray(field.fields)) {
        errs.push(`数组字段 ${field.name} 的 fields 必须是数组`);
      }
    }

    if (field.type === "object") {
      if ("size" in field && field.size) {
        errs.push(`对象字段 ${field.name} 不应包含 size`);
      }
      if (!Array.isArray(field.fields)) {
        errs.push(`对象字段 ${field.name} 的 fields 必须是数组`);
      }
    }
  }

  return errs;
}

let bodyLen = 0;
// -----------------------------------------------------------
// 字节解析（小端序）
// -----------------------------------------------------------
function parseBytesLE(bytes: any, struct: any) {
  let offset = 0;
  const dataView = new DataView(bytes.buffer);
  const result: any = {};

  // 辅助函数：解析单个字段
  function parseField(field: any, parentOffset: any, context: any) {
    let value;
    let fieldOffset = parentOffset;
    let temp = ""

    switch (field.type) {
      case "u8":
        value = dataView.getUint8(fieldOffset);
        temp = '【' + fieldOffset + '-' + (fieldOffset + field.size) + '】' + " u8 " + field.name + ": " + value + (field.description ? "  # " + field.description : '');
        console.log(temp);
        consoleLog += temp + '\n'
        fieldOffset += 1;

        if (field.name == 'msgType') {
          consoleLog += '\n -------------------- \n\n'
        }

        break;
      case "u16":
        value = dataView.getUint16(fieldOffset, true);
        temp = '【' + fieldOffset + '-' + (fieldOffset + field.size) + '】' + " u16 " + field.name + ": " + value + (field.description ? "  # " + field.description : '');
        console.log(temp);
        consoleLog += temp + '\n'
        fieldOffset += 2;
        if (field.name == 'bodyLen') {
          bodyLen = value;
        }
        break;
      case "u32":
        value = dataView.getUint32(fieldOffset, true);
        temp = '【' + fieldOffset + '-' + (fieldOffset + field.size) + '】' + " u32 " + field.name + ": " + value + (field.description ? "  # " + field.description : '');
        console.log(temp);
        consoleLog += temp + '\n'
        fieldOffset += 4;
        break;
      case "bytes":
        // 读取指定长度的字节并转换为字符串
        const byteArray = new Uint8Array(bytes.buffer, fieldOffset, field.size);
        let hexStr = Array.from(byteArray)
            .map(byte => byte.toString(16).padStart(2, '0'))
            .join(' ');

        value = hexStr;
        temp = '【' + fieldOffset + '-' + (fieldOffset + field.size) + '】' + " bytes " + field.name + ": " + hexStr + (field.description ? "  # " + field.description : '');
        console.log(temp);
        consoleLog += temp + '\n'
        fieldOffset += field.size;
        break;
      case "string":
        const stringByteArray = new Uint8Array(bytes.buffer, fieldOffset, field.size);
        // 读取指定长度的字节并转换为字符串
        value = new TextDecoder().decode(stringByteArray);
        temp = '【' + fieldOffset + '-' + (fieldOffset + field.size) + '】' + +" bytes " + field.name + ": " + value + (field.description ? "  # " + field.description : '');
        console.log(temp);
        consoleLog += temp + '\n'
        fieldOffset += field.size;
        break;
      case "greedyBytes":
        let remainSize = bodyLen - (fieldOffset - 5);
        temp = "\n\n开始解析最后的字节 " + field.name + '  fieldOffset: ' + fieldOffset + '  bodyLen: ' + bodyLen + '  remainSize: ' + remainSize;
        consoleLog += temp + '\n'
        // 读取指定长度的字节并转换为字符串
        const greedyByteArray = new Uint8Array(bytes.buffer, fieldOffset, remainSize);
        let greedyHexStr = Array.from(greedyByteArray)
            .map(byte => byte.toString(16).padStart(2, '0'))
            .join(' ');
        value = greedyHexStr;

        temp = "剩余字节 " + field.name + ": " + value;
        console.log(temp);
        consoleLog += temp + '\n'
        fieldOffset += field.size;
        break;
      case "array":
        // 查找count_from的值，先在当前上下文查找，再在顶层结果查找
        let count = context[field.count_from];
        if (count === undefined) {
          count = result[field.count_from];
        }
        const arr = [];

        for (let i = 0; i < count; i++) {
          const itemResult = {};
          // 解析数组元素的每个字段，使用当前元素作为上下文
          for (const subField of field.fields) {
            consoleLog += '  '
            const [subValue, subOffset] = parseField(subField, fieldOffset, itemResult);
            itemResult[subField.name] = subValue;
            fieldOffset = subOffset;
          }
          arr.push(itemResult);
        }
        value = arr;
        break;
      case "object":
        const obj = {};
        // 解析对象的每个字段，使用当前对象作为上下文
        for (const subField of field.fields) {
          const [subValue, subOffset] = parseField(subField, fieldOffset, obj);
          obj[subField.name] = subValue;
          fieldOffset = subOffset;
        }
        value = obj;
        break;
      default:
        value = null;
    }
    return [value, fieldOffset];
  }

  // 按照协议结构顺序依次解析所有字段，使用result作为顶层上下文
  for (const field of struct) {
    const [value, newOffset] = parseField(field, offset, result);
    result[field.name] = value;
    offset = newOffset;
  }

  return result;
}

// -----------------------------------------------------------
// 执行解析
// -----------------------------------------------------------
function parse() {
  // 重置日志
  consoleLog = "";
  
  let struct = [];
  try {
    struct = JSON.parse(protocolText.value);
  } catch (e) {
    errors.value = ["协议结构 JSON 格式错误"];
    return;
  }

  errors.value = validateProtocolStructure(struct);
  if (errors.value.length > 0) {
    return;
  }

  // 清理并验证 HEX 输入
  let clean = hexInput.value.replace(/[^0-9a-fA-F]/g, "");

  // 必须是偶数长度
  if (clean.length % 2 !== 0) {
    errors.value = ["Hex 数据长度不是偶数，可能缺少一个半字节"];
    return;
  }

  // 转成字节数组
  const byteArr = [];
  for (let i = 0; i < clean.length; i += 2) {
    const hexByte = clean.slice(i, i + 2);
    const num = Number.parseInt(hexByte, 16);

    if (Number.isNaN(num)) {
      errors.value = [`无效 HEX 字节: ${hexByte}`];
      return;
    }

    byteArr.push(num);
  }

  const bytes = new Uint8Array(byteArr);

  try {
    let result = parseBytesLE(bytes, struct);
    consoleLog += '\n\n\n JSON 格式结果：';
    consoleLog += JSON.stringify(result, null, 2);
  } catch (e) {
    errors.value = [`解析错误: ${e.message}`];
  }
}

async function updateProtocolItem() {
  let res: any = await getProtocolItemById(protocolId)
  if (res.data.code == 200) {
    protocolItem.value = res.data.data;
  } else {
    message("获取协议信息失败： " + res.data.msg, 'error');
  }

}

onMounted(async () => {
  if (protocolId != '') {
    await updateProtocolItem()
  }
})

</script>

<style scoped>
.protocol-parser {
  font-family: Arial;
  padding: 20px;
  height: 90vh;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
}

.left-panel-title {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
}
.input-area {
  width: 100%;
  height: 100%;
  font-family: monospace;
  resize: none;
  box-sizing: border-box;
}

.btn {
  margin: 10px 0;
  padding: 6px 12px;
  align-self: flex-start;
}

/* 底部左右布局 */
.bottom-container {
  display: flex;
  flex: 1;
  gap: 20px;
  padding: 0 15px 15px;
  overflow: hidden;
}

/* 左侧面板 */
.left-panel {
  flex: 1;
  background-color: #ffffff;
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  padding: 15px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.left-panel h3 {
  margin-top: 0;
  margin-bottom: 10px;
  color: #303133;
  font-size: 16px;
}

/* 右侧面板 */
.right-panel {
  flex: 1;
  background-color: #ffffff;
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  padding: 15px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.right-panel h3 {
  margin-top: 0;
  margin-bottom: 10px;
  color: #303133;
  font-size: 16px;
}

/* 面板内容 */
.panel-content {
  display: flex;
  flex-direction: column;
  height: 100%;
  gap: 15px;
  overflow: hidden;
}

/* textarea 包装器 */
.textarea-wrapper {
  flex: 1;
  min-height: 100px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* 小尺寸 textarea 包装器 */
.textarea-wrapper.small {
  flex: 0 0 auto;
  max-height: 100px;
}

.textarea-wrapper.small .input-area {
  min-height: 60px;
}

</style>